#include <iostream>
#include "Master.h"

using namespace std;

Master::Master(int port)
{
	server.init(port);
}

Master::~Master()
{
	server.closeSock();
}

void Master::start()
{
	pthread_t waitCo;
	pthread_create(&waitCo, NULL, waitConnections, (void*)(this));
	
	string line = "";
	getline(cin, line);
	while(line != "exit")
	{
		cout << line << endl;
		getline(cin, line);
	}
	
	pthread_cancel(waitCo);
}
		
void *Master::waitConnections(void *arg)
{
	Master *m = (Master*)(arg);
	while(1)
	{
		SocketServer sock;
		string msg = "";
		long size = 0;
		int status = 0;
		
		sock = m->server.accept_connection();
		void *param[2];
		param[0] = m;
		param[1] = &sock;
		pthread_t co;
		pthread_create(&co, NULL, connect, param);
	}
}

void *Master::connect(void *arg)
{
	void **param = (void**)(arg);
	Master *m = (Master*)(param[0]);
	SocketServer *sock = (SocketServer*)(param[1]);
	
	string msg = "";
	int status = m->getProto(msg, sock);
	long size = 0;
	
	if(status > 0 && msg == "OBS/LOGIN")
	{
		string login = "";
		sock->read4(&size);
		status = m->getString(login, size, sock);
		if(status > 0 && !m->checkLogin(login))
		{
			obs o = { *sock, login, "" };
			m->obss.push_back(o);
			void *param[2];
			param[0] = (void*)(m);
			param[1] = (void *)(&o);
			pthread_create(&o.thread_obs, NULL, manageObs, (void*)(param));
			pthread_join(o.thread_obs, NULL);
		}
		else
			m->sendProto("OBS/ERR/LOGIN", sock);
	}
	else if(msg == "ETUD/LOGIN")
	  {
	    string login = "";
	    sock->read4(&size);
	    status = m->getString(login, size, sock);
	    if(status > 0 && !m->checkLogin(login))
	      {
			etu e = { *sock, login, "", "" };
			m->etus.push_back(e);
			void *param[2];
			param[0] = (void*)(m);
			param[1] = (void *)(&e);
			pthread_create(&e.thread_etu, NULL, manageEtu, (void*)(param));
			pthread_join(e.thread_etu, NULL);
	      }
	    else
	      m->sendProto("ETUD/ERR/LOGIN", sock);
	  }
	else if(msg == "AST/LOGIN")
	{
		string login = "", etulogin = "";
		
	    sock->read4(&size);
	    status = m->getString(login, size, sock);
	    if(status > 0 && !m->checkLogin(login))
	      {
				sock->read4(&size);
				status = m->getString(etulogin, size, sock);
				if(status > 0 && m->checkLogin(etulogin))
				{
					bool etuoccupe = false;
					for(vector<ast>::iterator it = m->asts.begin(); it != m->asts.end() && !etuoccupe; ++it)
					{
						if(it->etulogin == etulogin)
							etuoccupe = true;
					}
					
					if(etuoccupe)
					{
						m->sendProto("AST/ERR/LOGIN", sock);
						return NULL;
					}

					m->sendProto("AST/OK/LOGIN", sock);
					ast a = { *sock, login, etulogin };
					m->asts.push_back(a);
					cout << "Un nouvel assistant est en ligne : " << a.login << endl;
					void *param[2];
					param[0] = (void*)(m);
					param[1] = (void *)(&a);
					pthread_create(&a.thread_ast, NULL, manageAst, (void*)(param));
					pthread_join(a.thread_ast, NULL);
				}
				else
					return NULL;
	      }
	    else
	      return NULL;
	     
	     
	}
	else 
	  cout<<"unknown message"<<endl;
}

bool Master::checkLogin(const string &login)
{
	bool find = false;
	obsIter ito, itoEnd = obss.end();
	etuIter ite, iteEnd = etus.end();
	astIter ita, itaEnd = asts.end();
	for(ito = obss.begin(); ito != itoEnd && !find; ++ito)
	{
		if(ito->login == login)
			find = true;
	}
	for(ite = etus.begin(); ite != iteEnd && !find; ++ite)
	{
		if(ite->login == login)
			find = true;
	}
	for(ita = asts.begin(); ita != itaEnd && !find; ++ita)
	{
		if(ita->login == login)
			find = true;
	}
	return find;
}

bool Master::checkRoom(const string &name)
{
	bool find = false;
	roomIter it, itEnd = rooms.end();
	for(it = rooms.begin(); it != itEnd && !find; ++it)
	{
		if(it->name == name)
			find = true;
	}
	return find;
}

int Master::getProto(string &msg, Socket *sock)
{
	char id;
	bool find = false;
	int nb = sock->readMsg(&id, 1);
	map<string, char>::iterator it;
	for(it = proto._messages.begin(); it != proto._messages.end() && !find; ++it)
	{
		if(it->second == id)
		{
			msg = it->first;
			find = true;
		}
	}
	if(!find)
		msg = "unknown";
	return nb;
}

void Master::sendProto(const string msg, Socket *sock)
{
	try {
		char id = proto[msg];
		sock->sendMsg((void*)(&id), 1);
	}
	catch(exception e) {
		cout << "Unknown message" << endl;
	}
}

int Master::getString(string &msg, int size, Socket *sock, int bufsize)
{
	char s[bufsize];
	int nb = sock->readMsg(s, size);
	s[size] = 0;
	msg = s;
	return nb;
}
